module WS

import ..Model
const Trade = Model.Trade

using Dates
using WebSockets
using Lazy
import Reactive; const R = Reactive
import JSON
using ..Helpers

const BxDateTimeFormat = dateformat"y-m-dTH:M:S.sZ"

const TradeOrNothing = Union{Trade, Nothing}

function parse_bx_datetime(timestamp :: String) :: DateTime
  DateTime(timestamp, BxDateTimeFormat)
end

function bx_insert_to_trade(msg_data) :: Trade
  time = @> msg_data["timestamp"] parse_bx_datetime
  Trade(msg_data["price"], msg_data["size"], time)
end

function is_bx_insert(msg) :: Bool
  has_and_eq(msg, "table", "trade") &&
  has_and_eq(msg, "action", "insert")
end

function is_bx_partial(msg) :: Bool
  has_and_eq(msg, "table", "trade") &&
  has_and_eq(msg, "action", "partial")
end

function is_bx_welcome_msg(msg) :: Bool
  has_and_eq(msg, "info", "Welcome to the BitMEX Realtime API.")
end

function is_bx_subscribe(msg) :: Bool
  haskey(msg, "subscribe") &&
  has_and_eq(msg, "success", true)
end

function is_bx_aux_message(msg) :: Bool
  is_bx_subscribe(msg) || is_bx_welcome_msg(msg)
end

function handle_bx_trade_message!(msg, trade_signal :: R.Signal{TradeOrNothing})
  push_to_signal!(trade :: Trade) = push!(trade_signal, trade)
  msg_data = msg["data"]
  @>> begin
    msg_data
    map(bx_insert_to_trade)
    map(doon(debug("new trade")))
    foreach(push_to_signal!)
  end
end

function feed_trades!(symbol :: String,
                      trade_signal :: R.Signal{TradeOrNothing})
  #trade_signal = R.Signal(TradeOrNothing, nothing)
  uri = "wss://www.bitmex.com/realtime?subscribe=trade:" * symbol
  WebSockets.open(uri) do ws
    i = 0
    while i < 15
      i = i + 1
      sleep(0.5)
      msg, success = WebSockets.readguarded(ws)
      if !success
        @error "listen_on_trades: not success"
        return
      end
      msg = @> msg String JSON.parse
      if is_bx_insert(msg) || is_bx_partial(msg)
        handle_bx_trade_message!(msg, trade_signal)
      elseif is_bx_aux_message(msg)
        debug("aux message", msg)
      else
        @error "ws unexpected" msg
        return
      end
    end
  end
end

end

# TODO
# na rodos kad partial ne visur vienu metu tokios pacios vertes
# ar push! suveikia ne is karto, ir programa naudoja sena partial verte.
# reikia pergalvoti koda
